package com.zenika.zenbinding.client.util;

import com.google.gwt.core.client.GWT;
import com.zenika.zenbinding.client.converter.Converter;
import com.zenika.zenbinding.client.converter.Converters;
import com.zenika.zenbinding.client.exception.ConverterException;
import com.zenika.zenbinding.client.exception.DataException;
import com.zenika.zenbinding.client.model.BusinessObject;

/**
 * This utility class allows to :
 * <ul>
 * <li> Get and set a property of an object with a pointed annotation (like
 * "address.streetName"). </li>
 * <li> Get the type name of an object </li>
 * <li> Get a converter matching to a property </li>
 * </ul>
 * 
 * @author Zenika
 */
public class BeanUtil {

	private static WrapperLoader loader;
	static {
		loader = (WrapperLoader) GWT.create(WrapperLoader.class);
	}

	/**
	 * Wrap an object and return it once wrapped. A wrapper permits to set and
	 * get properties of an object by their name.
	 * 
	 * @param object
	 *            The object to wrap.
	 * @return The wrapper containing the object given in parameter.
	 */
	public static Wrapper getWrapper(BusinessObject object) {
		String className = GWT.getTypeName(object);
		Wrapper wrapper = loader.get(className);
		wrapper.setContent(object);
		return wrapper;
	}

	public static Object getProperty(BusinessObject source, String propertyPath)
			throws DataException {
		String[] objects = decompPath(propertyPath);
		Wrapper wrapper = getLastWrapper(objects, source);
		String propertyName = objects[objects.length - 1];
		Object result = null;
		result = wrapper.getProperty(propertyName);
		return result;

	}

	public static boolean isPropertyNull(BusinessObject source,
			String propertyPath) throws DataException {
		String[] objects = decompPath(propertyPath);
		Wrapper wrapper = getLastWrapper(objects, source);
		String propertyName = objects[objects.length - 1];
		// Object result = null;
		// result = wrapper.isPropertyNull(propertyName);
		return wrapper.isPropertyNull(propertyName);

	}

	public static void setProperty(BusinessObject source, String propertyPath,
			Object target) {
		String[] objects = decompPath(propertyPath);
		Wrapper wrapper = getLastWrapper(objects, source);
		String propertyName = objects[objects.length - 1];
		try {
			wrapper.setProperty(propertyName, target);
		} catch (DataException e) {
			e.printStackTrace();
		}

	}

	private static String[] decompPath(String propertyPath) {
		String[] objects = propertyPath.split("\\.");
		return objects;
	}

	private static Wrapper getLastWrapper(String[] objects,
			BusinessObject source) {
		Wrapper wrapper = getWrapper(source);
		for (int i = 0; i < objects.length - 1; i++) {
			String propertyName = objects[i];
			Object property = null;
			try {
				property = wrapper.getProperty(propertyName);
			} catch (DataException e) {
				e.printStackTrace();
			}
			wrapper = getWrapper((BusinessObject) property);
		}
		return wrapper;
	}

	public static String getTypeName(BusinessObject object, String propertyPath) {
		Object obj = null;
		if (propertyPath != null) {
			try {
				obj = getProperty(object, propertyPath);
			} catch (DataException e) {
				e.printStackTrace();
			}
		} else {
			obj = object;
		}
		String className = GWT.getTypeName(obj);
		return className;
	}

	public static Converters converters;
	static {
		converters = Converters.getInstance();
	}

	/**
	 * Returns the Converter matching with the type of a property.
	 * 
	 * @param object
	 *            The object containing the property
	 * @param propertyPath
	 *            The property path;
	 * @return The corresponding converter
	 * @throws DataException
	 */
	public static Converter getDefaultConverter(BusinessObject object,
			String propertyPath) throws DataException {
		String type = getTypeName(object, propertyPath);
		return findConverter(type);
	}

	/**
	 * Return the converter corresponding to an object.
	 * 
	 * @param object
	 * @return The good converter
	 * @throws DataException
	 */
	public static Converter getDefaultConverter(Object object)
			throws DataException {
		String type = GWT.getTypeName(object);
		return findConverter(type);
	}

	private static Converter findConverter(String type) throws DataException {
		if (type.equals("java.lang.String")) {
			return converters.STRING;
		} else if (type.equals("java.lang.Integer")) {
			return converters.INTEGER;
		} else if (type.equals("java.lang.Boolean")) {
			return converters.BOOLEAN;
		} else if (type.equals("java.lang.Double")) {
			return converters.DOUBLE;
		} else if (type.equals("java.lang.Float")) {
			return converters.FLOAT;
		} else if (type.equals("java.lang.Short")) {
			return converters.SHORT;
		} else if (type.equals("java.lang.Long")) {
			return converters.LONG;
		} else if (type.equals("java.lang.Byte")) {
			return converters.BYTE;
		} else if (type.equals("java.lang.Character")) {
			return converters.CHARACTER;
		} else if (type.equals("java.util.Date")) {
			return converters.DATE;
		} else {
			throw new ConverterException(
					"No converter found. Was your property a primitive or a primitive wrapper?");
		}
	}

	public static BusinessObject clone(BusinessObject object) {
		Wrapper wrapper = getWrapper(object);
		return (BusinessObject) wrapper.clone();
	}

}
